/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package RunnerGameStates;

import com.jme.input.*;
import com.jme.input.controls.*;
import com.jme.input.controls.binding.*;
import com.jme.input.controls.controller.*;
import com.jme.math.*;
import com.jme.renderer.*;
import com.jme.scene.CameraNode;
import com.jme.system.*;
import com.jme.util.*;
import com.jmex.game.state.*;
import java.util.concurrent.*;
import java.util.logging.*;
import runner.*;

/**
 *
 * @author 1130608864
 */
public class BalinerasGameState extends BasicGameState {

  private CameraNode camNode;
  private CameraNode topCamNode;
  private CameraNode outdoorCamNode;
  private CameraNode indoorCamNode;
  private Camera cam;
  private Camera topCam;
  private Camera outdoorCam;
  private Camera indoorCam;
  private KeyBindingManager keyBndMng = KeyBindingManager.getKeyBindingManager();
  private RelativeMouse mouse;
  private InputHandler mouseInputHandler = new InputHandler();
  //private FirstPersonHandler fpHandler;
  private float pitch;
  private float yaw;
  private float topCamRot;
  private boolean topCamDir;

  public BalinerasGameState(String name) {
    super(name);
    buildCamera();
    buildTopCamera();
    buildOutdoorCamera();
    buildIndoorCamera();
    passRendererCamera();
    initKeyBinding();
    initMouseBinding();
    this.topCamRot = 0.0f;
    this.topCamDir = true;
  }

  public void buildCamera() {
    Future<Camera> future = GameTaskQueueManager.getManager().update(new Callable<Camera>() {

      @Override
      public Camera call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        int width = display.getWidth();
        int height = display.getHeight();
        Camera camera = display.getRenderer().createCamera(width, height);
        camera.setFrustumPerspective(45.0f, (float) width / (float) height, 1, 1000);
        return camera;
      }
    });
    try {
      Camera myCamera = future.get();
      this.cam = myCamera;
      this.camNode = new CameraNode("Camera", this.cam);
      Vector3f camLocation = this.camNode.getLocalTranslation().clone();
      this.camNode.setLocalTranslation(camLocation.add(0f, 6.0f, -35.0f));
      this.rootNode.attachChild(camNode);
    } catch (InterruptedException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ExecutionException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public void buildTopCamera() {
    Future<Camera> future = GameTaskQueueManager.getManager().update(new Callable<Camera>() {

      @Override
      public Camera call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        int width = display.getWidth();
        int height = display.getHeight();
        Camera camera = display.getRenderer().createCamera(width, height);
        camera.setFrustumPerspective(45.0f, (float) width / (float) height, 1, 1000);
        return camera;
      }
    });
    try {
      Camera myCamera = future.get();
      this.topCam = myCamera;
      this.topCamNode = new CameraNode("TopCamera", this.topCam);
      Vector3f camLocation = this.topCamNode.getLocalTranslation().clone();
      this.topCamNode.setLocalTranslation(camLocation.add(0f, 125.0f, -75.0f));
      this.getRootNode().attachChild(topCamNode);
    } catch (InterruptedException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ExecutionException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public void buildOutdoorCamera() {
    Future<Camera> future = GameTaskQueueManager.getManager().update(new Callable<Camera>() {

      @Override
      public Camera call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        int width = display.getWidth();
        int height = display.getHeight();
        Camera camera = display.getRenderer().createCamera(width, height);
        camera.setFrustumPerspective(45.0f, (float) width / (float) height, 1, 1000);
        return camera;
      }
    });
    try {
      Camera myCamera = future.get();
      this.outdoorCam = myCamera;
      this.outdoorCamNode = new CameraNode("OutdoorCamera", this.outdoorCam);
      Vector3f camLocation = this.outdoorCamNode.getLocalTranslation().clone();
      this.outdoorCamNode.setLocalTranslation(camLocation.add(0f, 400.0f, -500.0f));
      this.getRootNode().attachChild(outdoorCamNode);
    } catch (InterruptedException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ExecutionException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public void buildIndoorCamera() {
    Future<Camera> future = GameTaskQueueManager.getManager().update(new Callable<Camera>() {

      @Override
      public Camera call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        int width = display.getWidth();
        int height = display.getHeight();
        Camera camera = display.getRenderer().createCamera(width, height);
        camera.setFrustumPerspective(45.0f, (float) width / (float) height, 1, 1000);
        return camera;
      }
    });
    try {
      Camera myCamera = future.get();
      this.indoorCam = myCamera;
      this.indoorCamNode = new CameraNode("IndoorCamera", this.indoorCam);
      Vector3f camLocation = this.indoorCamNode.getLocalTranslation().clone();
      this.indoorCamNode.setLocalTranslation(camLocation.add(0f, 40.0f, -50.0f));
      this.getRootNode().attachChild(indoorCamNode);
    } catch (InterruptedException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ExecutionException ex) {
      Logger.getLogger(EscenaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  public void passRendererCamera() {
    GameTaskQueueManager.getManager().update(new Callable() {

      public Void call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        display.getRenderer().setCamera(cam);
        return null;
      }
    });
  }

  public void passRendererTopCamera() {
    GameTaskQueueManager.getManager().update(new Callable() {

      public Void call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        display.getRenderer().setCamera(topCam);
        return null;
      }
    });
  }

  public void passRendererOutdoorCamera() {
    GameTaskQueueManager.getManager().update(new Callable() {

      public Void call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        Quaternion pitchQuat = outdoorCamNode.getLocalRotation().clone();//Up - Down
        Quaternion pitchRotated = pitchQuat.fromAngleAxis((float) Math.PI / 4, Vector3f.UNIT_X);
        outdoorCamNode.setLocalRotation(pitchRotated);
        display.getRenderer().setCamera(outdoorCam);
        return null;
      }
    });
  }

  public void passRendererIndoorCamera() {
    GameTaskQueueManager.getManager().update(new Callable() {

      public Void call() throws Exception {
        DisplaySystem display = DisplaySystem.getDisplaySystem();
        Quaternion pitchQuat = indoorCamNode.getLocalRotation().clone();//Up - Down
        Quaternion pitchRotated = pitchQuat.fromAngleAxis((float) Math.PI / 4, Vector3f.UNIT_X);
        indoorCamNode.setLocalRotation(pitchRotated);
        display.getRenderer().setCamera(indoorCam);
        return null;
      }
    });
  }

  public void initKeyBinding() {
    keyBndMng.set("Up", KeyInput.KEY_UP);
    keyBndMng.set("Down", KeyInput.KEY_DOWN);
    keyBndMng.set("Left", KeyInput.KEY_LEFT);
    keyBndMng.set("Right", KeyInput.KEY_RIGHT);
    keyBndMng.set("Cam01", KeyInput.KEY_1);
    keyBndMng.set("Cam02", KeyInput.KEY_2);
    keyBndMng.set("Cam03", KeyInput.KEY_3);
    keyBndMng.set("Cam04", KeyInput.KEY_4);
  }

  public void initMouseBinding() {
    this.mouse = new RelativeMouse("Mouse");
    this.mouse.registerWithInputHandler(mouseInputHandler);
    this.pitch = 0f;
    this.yaw = 0f;
    //this.fpHandler = new FirstPersonHandler(this.cam);
  }

  @Override
  public void update(float tpf) {
    super.update(tpf);
    Vector3f camLocation = this.camNode.getLocalTranslation().clone();
    if (keyBndMng.isValidCommand("Up")) {
      this.camNode.setLocalTranslation(camLocation.add(new Vector3f(0f, 0f, 0.5f)));
    } else if (keyBndMng.isValidCommand("Down")) {
      this.camNode.setLocalTranslation(camLocation.add(new Vector3f(0f, 0f, -0.5f)));
    } else if (keyBndMng.isValidCommand("Left")) {
      this.camNode.setLocalTranslation(camLocation.add(new Vector3f(0.5f, 0f, 0f)));
    } else if (keyBndMng.isValidCommand("Right")) {
      this.camNode.setLocalTranslation(camLocation.add(new Vector3f(-0.5f, 0f, 0f)));
    } else if (keyBndMng.isValidCommand("Cam01")) {
      passRendererCamera();
    } else if (keyBndMng.isValidCommand("Cam02")) {
      passRendererTopCamera();
    } else if (keyBndMng.isValidCommand("Cam03")) {
      passRendererOutdoorCamera();
    } else if (keyBndMng.isValidCommand("Cam04")) {
      passRendererIndoorCamera();
    }
    mouseInputHandler.update(tpf);
    float x = mouse.getLocalTranslation().x;
    float y = mouse.getLocalTranslation().y;

    if (y > 0) {
      if (!((pitch + Math.PI / 180) > Math.PI / 3)) {
        pitch = (pitch + (float) Math.PI / 180);
      }
    } else if (y < 0) {
      if (!((pitch - Math.PI / 180) < -Math.PI / 3)) {
        pitch = (pitch - (float) Math.PI / 180);
      }
    }

    if (x > 0) {
      if (!(yaw + (float) Math.PI / 180 > Math.PI / 2)) {
        yaw = (yaw + (float) Math.PI / 180);
      }
    } else if (x < 0) {
      if (!(yaw - (float) Math.PI / 180 < -Math.PI / 2)) {
        yaw = (yaw - (float) Math.PI / 180);
      }
    }


    this.topCamRot = (this.topCamDir ? this.topCamRot + (tpf * 0.05f) : this.topCamRot - (tpf * 0.05f));

    if (Math.abs(this.topCamRot) > Math.PI / 7) {
      this.topCamDir = !this.topCamDir;
    }

    Quaternion topCamNodeQuat = this.topCamNode.getLocalRotation();
    Quaternion topCamRotY = topCamNodeQuat.fromAngleAxis(this.topCamRot, Vector3f.UNIT_Y);
    this.topCamNode.setLocalRotation(topCamRotY);


    Quaternion topCamPitchQuat = outdoorCamNode.getLocalRotation().clone();//Up - Down
    Quaternion topCamPitchRotated = topCamPitchQuat.fromAngleAxis((float) Math.PI / 4, Vector3f.UNIT_X);

    Matrix3f topCamPitchMatrix = topCamPitchRotated.toRotationMatrix();
    Matrix3f topCamRotYMatrix = topCamRotY.toRotationMatrix();

    Matrix3f topCamRotMatrix = topCamPitchMatrix.mult(topCamRotYMatrix);

    Quaternion topCamRotQuat = new Quaternion();
    this.topCamNode.setLocalRotation(topCamRotQuat.fromRotationMatrix(topCamRotMatrix));

    Quaternion yawQuat = this.camNode.getLocalRotation().clone();//Left - Right
    Quaternion yawRotated = yawQuat.fromAngleAxis(-yaw, Vector3f.UNIT_Y);
    //this.camNode.setLocalRotation(yawRotated);

    Quaternion pitchQuat = this.camNode.getLocalRotation().clone();//Up - Down
    Quaternion pitchRotated = pitchQuat.fromAngleAxis(-pitch, Vector3f.UNIT_X);
    //this.camNode.setLocalRotation(pitchRotated);

    Matrix3f yawMatrix = yawRotated.toRotationMatrix();
    Matrix3f pitchMatrix = pitchRotated.toRotationMatrix();
    Matrix3f pitchYawMatrix = yawMatrix.mult(pitchMatrix);
    Quaternion q = new Quaternion();
    q.fromRotationMatrix(pitchYawMatrix);

    this.camNode.setLocalRotation(q);
  }

  @Override
  public void render(float tpf) {
    super.render(tpf);
    DisplaySystem.getDisplaySystem().getRenderer().setBackgroundColor(ColorRGBA.white);
  }


}
